#ifndef __COMPLEX_H__
#define __COMPLEX_H__

#include <cmath>
#include <complex>
#include <boost/math/complex.hpp>
#include <QSharedData>
#include <QSharedDataPointer>
#include <QString>

class Complex
{
public:
    /// Internal representation of a real number.
    typedef long double RealType;
    /// Internal representation of a complex number.
    typedef std::complex<RealType> ComplexType;

    /// Default constructor.
    Complex()
    { }
    /// Real constructor.
    Complex(
        /// The real part of the data.
        const RealType& real_part
    ) :
        d(real_part)
    { }
    /// Complex constructor.
    Complex(
        /// The real part of the data.
        const RealType& real_part,
        /// The imaginary part of the data.
        const RealType& imaginary_part
    ) :
        d(real_part, imaginary_part)
    { }
    /// Complex constructor 2.
    Complex(
        /// The real part of the data.
        const ComplexType& cmplx
    ) :
        d(cmplx)
    { }
    /// Empty destructor.
    ~Complex()
    { }
    /// Sets the new value of the Complex.
    void setValue(
        /// The new real part.
        RealType x,
        /// The new imaginary part.
        RealType y = 0.0)
    { d = ComplexType(x, y); }

    //--------------------------------------------------------------------------
    // Arithmetic operations
    //--------------------------------------------------------------------------
    /// \return \e this + \p other.
    Complex add(const Complex& other) const
    { return Complex(d + other.d); }
    /// \return \e this - \p other.
    Complex subtract(const Complex& other) const
    { return Complex(d - other.d); }
    /// \return -(\e this).
    Complex negate() const
    { return Complex(-d); }
    /// \return \e this x \p other.
    Complex multiply(const Complex& other) const
    { return Complex(d * other.d); }
    /// \return \e this / \p other.
    Complex divide(const Complex& other) const
    { return Complex(d / other.d); }
	/// \return 1 / \e this.
	Complex inverse() const
	{ return Complex(ComplexType(1.0) / d); }
    /// \return \e this ^ \p other (where ^ is the to-the-power-of operator).
    Complex pow(const Complex& other) const
    { return Complex(std::pow(d, other.d)); }
    /// \return the absolute Complex of \e this
    RealType abs() const
    { return std::abs(d); }
    /// \return the angle Theta \e this makes with the positive real axis on
    /// the complex plane.
    RealType arg() const
    { return std::arg(d); }
    /// \return the real part of \e this.
    RealType real() const
    { return d.real(); }
    /// \return the imaginary part of \e this.
    RealType imag() const
    { return d.imag(); }
    /// \return sqrt(\e this)
    Complex sqrt() const
    { return Complex(std::sqrt(d)); }
    /// \return the natural logarithm of \e this.
    Complex ln() const
    { return Complex(std::log(d)); }
    /// \return e ^ \e this (where e is Euler's number).
    Complex exp() const
    { return Complex(std::exp(d)); }
    /// \return the base 10 logarithm of \e this.
    Complex log10() const
    { return Complex(std::log10(d)); }
    /// \return the base \p base logarithm of \e this.
    Complex logx(const Complex& base) const
    { return Complex(std::log(d) / std::log(base.d)); }

    //--------------------------------------------------------------------------
    // Trigonometic operations
    //--------------------------------------------------------------------------
    /// \return sine(\e this).
    Complex sin() const
    { return Complex(std::sin(d)); }
    Complex sind() const
    { return Complex(std::sin(d_to_r * d)); }
    /// \return cosine(\e this).
    Complex cos() const
    { return Complex(std::cos(d)); }
    Complex cosd() const
    { return Complex(std::cos(d_to_r * d)); }
    /// \return tangent(\e this).
    Complex tan() const
    { return Complex(std::tan(d)); }
    Complex tand() const
    { return Complex(std::tan(d_to_r * d)); }
    /// \return secant(\e this).
    Complex sec() const
    { return Complex(std::cos(d)).inverse(); }
    Complex secd() const
    { return Complex(std::cos(d_to_r * d)).inverse(); }
    /// \return cosecant(\e this).
    Complex csc() const
    { return Complex(std::sin(d)).inverse(); }
    Complex cscd() const
    { return Complex(std::sin(d_to_r * d)).inverse(); }
    /// \return cotangent(\e this).
    Complex cot() const
    { return Complex(std::tan(ComplexType(M_PI_2) - d)); }
    Complex cotd() const
    { return Complex(std::tan(ComplexType(M_PI_2) - d_to_r * d)); }
    /// \return sin(\e this) / \e this.
    Complex sinc() const
    { return Complex(std::sin(d) / d); }
    /// \return sin(pi * \e this) / (pi * \e this).
    Complex sincn() const
    { return multiply(Complex(M_PI)).sinc(); }
    /// \return arcsine(\e this).
    Complex asin() const
    { return Complex(boost::math::asin(d)); }
    Complex asind() const
    { return Complex(r_to_d * asin().d); }
    /// \return arccosine(\e this).
    Complex acos() const
    { return Complex(boost::math::acos(d)); }
    Complex acosd() const
    { return Complex(r_to_d * acos().d); }
    /// \return arctangent(\e this).
    Complex atan() const
    { return Complex(boost::math::atan(d)); }
    Complex atand() const
    { return Complex(r_to_d * atan().d); }
    /// \return arcsecant(\e this).
    Complex asec() const
    { return inverse().acos(); }
    Complex asecd() const
    { return inverse().acosd(); }
    /// \return arccosecant(\e this).
    Complex acsc() const
    { return inverse().asin(); }
    Complex acscd() const
    { return Complex(r_to_d * boost::math::asin(inverse().d)); }
    /// \return arccotangent(\e this).
    Complex acot() const
    { return Complex(ComplexType(M_PI_2) - atan().d); }
    Complex acotd() const
    { return Complex(ComplexType(90.0) -  atand().d); }

    //--------------------------------------------------------------------------
    // Hyperbolic operations
    //--------------------------------------------------------------------------
    /// \return sineh(\e this).
    Complex sinh() const
    { return Complex(std::sinh(d)); }
    /// \return cosineh(\e this).
    Complex cosh() const
    { return Complex(std::cosh(d)); }
    /// \return tangenth(\e this).
    Complex tanh() const
    { return Complex(std::tanh(d)); }
    /// \return secanth(\e this).
    Complex sech() const
    { return Complex(std::cosh(d)).inverse(); }
    /// \return cosecanth(\e this).
    Complex csch() const
    { return Complex(std::sinh(d)).inverse(); }
    /// \return cotangenth(\e this).
    Complex coth() const
    { return Complex(std::tanh(d)).inverse(); }
    /// \return arcsineh(\e this).
    Complex asinh() const
    { return Complex(boost::math::asinh(d)); }
    /// \return arccosineh(\e this).
    Complex acosh() const
    { return Complex(boost::math::acosh(d)); }
    /// \return arctangenth(\e this).
    Complex atanh() const
    { return Complex(boost::math::atanh(d)); }
    /// \return arcsecanth(\e this).
    Complex asech() const
    { return Complex(boost::math::acosh(inverse().d)); }
    /// \return arccosecanth(\e this).
    Complex acsch() const
    { return Complex(boost::math::asinh(inverse().d)); }
    /// \return arccotangenth(\e this).
    Complex acoth() const
    { return Complex(boost::math::atanh(inverse().d)); }

    //--------------------------------------------------------------------------
    // Operator aliases for improved readability
    //--------------------------------------------------------------------------
    /// Assignment from Complex
    Complex& operator=(const Complex& other)
    {
        d = other.d;
        return *this;
	}
    /// Equality.
    bool operator==(const Complex& other) const
    { return d == other.d; }
    /// Inequality.
    bool operator!=(const Complex& other) const
    { return d != other.d; }
    /// \return \e this + \p other.
    Complex operator+(const Complex& other) const
    { return add(other); }
    /// \return \e this - \p other.
    Complex operator-(const Complex& other) const
    { return subtract(other); }
    /// \return -(\e this).
    Complex operator-() const
    { return negate(); }
    /// \return \e this x \p other.
    Complex operator*(const Complex& other) const
    { return multiply(other); }
    /// \return \e this / \p other.
    Complex operator/(const Complex& other) const
    { return divide(other); }

private:
    ComplexType d;

    static const RealType r_to_d;
    static const RealType d_to_r;

};

//------------------------------------------------------------------------------
// Arithmetic operations (functional style aliases for improved readability)
//------------------------------------------------------------------------------
/// \return the inverse of \e val
inline Complex inv(const Complex& val)
{ return val.inverse(); }
/// \return \e val ^ \p other (where ^ is the to-the-power-of operator).
inline Complex pow(const Complex& val, const Complex& other)
{ return val.pow(other); }
/// \return the absolute value of \e val
inline Complex::RealType abs(const Complex& val)
{ return val.abs(); }
/// \return the angle Theta \e val makes with the positive real axis on
/// the complex plane.
inline Complex::RealType arg(const Complex& val)
{ return val.arg(); }
/// \return the real part of \e val.
inline Complex::RealType real(const Complex& val)
{ return val.real(); }
/// \return the imaginary part of \e val.
inline Complex::RealType imag(const Complex& val)
{ return val.imag(); }
/// \return sqrt(\e val)
inline Complex sqrt(const Complex& val)
{ return val.sqrt(); }
/// \return the natural logarithm of \e val.
inline Complex ln(const Complex& val)
{ return val.ln(); }
/// \return e ^ \e val (where e is Euler's number).
inline Complex exp(const Complex& val)
{ return val.exp(); }
/// \return the base 10 logarithm of \e val.
inline Complex log10(const Complex& val)
{ return val.log10(); }
/// \return the base \p base logarithm of \e val.
inline Complex logx(const Complex& base, const Complex& val)
{ return val.logx(base); }

//------------------------------------------------------------------------------
// Trigonometic operations (functional style aliases for improved readability)
//------------------------------------------------------------------------------
/// \return sine(\p val).
inline Complex sin(const Complex& val)
{ return val.sin(); }
inline Complex sind(const Complex& val)
{ return val.sind(); }
/// \return cosine(\p val).
inline Complex cos(const Complex& val)
{ return val.cos(); }
inline Complex cosd(const Complex& val)
{ return val.cosd(); }
/// \return tangent(\p val).
inline Complex tan(const Complex& val)
{ return val.tan(); }
inline Complex tand(const Complex& val)
{ return val.tand(); }
/// \return secant(\p val).
inline Complex sec(const Complex& val)
{ return val.sec(); }
inline Complex secd(const Complex& val)
{ return val.secd(); }
/// \return cosecant(\p val).
inline Complex csc(const Complex& val)
{ return val.csc(); }
inline Complex cscd(const Complex& val)
{ return val.cscd(); }
/// \return cotangent(\p val).
inline Complex cot(const Complex& val)
{ return val.cot(); }
inline Complex cotd(const Complex& val)
{ return val.cotd(); }
/// \return sin(\p val) / \p val.
inline Complex sinc(const Complex& val)
{ return val.sinc(); }
/// \return sin(pi * \p val) / (pi * \p val).
inline Complex sincn(const Complex& val)
{ return val.sincn(); }
/// \return arcsine(\p val).
inline Complex asin(const Complex& val)
{ return val.asin(); }
inline Complex asind(const Complex& val)
{ return val.asind(); }
/// \return arccosine(\p val).
inline Complex acos(const Complex& val)
{ return val.acos(); }
inline Complex acosd(const Complex& val)
{ return val.acosd(); }
/// \return arctangent(\p val).
inline Complex atan(const Complex& val)
{ return val.atan(); }
inline Complex atand(const Complex& val)
{ return val.atand(); }
/// \return arcsecant(\p val).
inline Complex asec(const Complex& val)
{ return val.asec(); }
inline Complex asecd(const Complex& val)
{ return val.asecd(); }
/// \return arccosecant(\p val).
inline Complex acsc(const Complex& val)
{ return val.acsc(); }
inline Complex acscd(const Complex& val)
{ return val.acscd(); }
/// \return arccotangent(\p val).
inline Complex acot(const Complex& val)
{ return val.acot(); }
inline Complex acotd(const Complex& val)
{ return val.acotd(); }

//------------------------------------------------------------------------------
// Hyperbolic operations (functional style aliases for improved readability)
//------------------------------------------------------------------------------
/// \return sineh(\p val).
inline Complex sinh(const Complex& val)
{ return val.sinh(); }
/// \return cosineh(\p val).
inline Complex cosh(const Complex& val)
{ return val.cosh(); }
/// \return tangenth(\p val).
inline Complex tanh(const Complex& val)
{ return val.tanh(); }
/// \return secanth(\p val).
inline Complex sech(const Complex& val)
{ return val.sech(); }
/// \return cosecanth(\p val).
inline Complex csch(const Complex& val)
{ return val.csch(); }
/// \return cotangenth(\p val).
inline Complex coth(const Complex& val)
{ return val.coth(); }
/// \return arcsineh(\p val).
inline Complex asinh(const Complex& val)
{ return val.asinh(); }
/// \return arccosineh(\p val).
inline Complex acosh(const Complex& val)
{ return val.acosh(); }
/// \return arctangenth(\p val).
inline Complex atanh(const Complex& val)
{ return val.atanh(); }
/// \return arcsecanth(\p val).
inline Complex asech(const Complex& val)
{ return val.asech(); }
/// \return arccosecanth(\p val).
inline Complex acsch(const Complex& val)
{ return val.acsch(); }
/// \return arccotangenth(\p val).
inline Complex acoth(const Complex& val)
{ return val.acoth(); }

#endif
